package redecolaborativa.server.objetos;



import java.io.Serializable;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.LinkedList;

import redecolaborativa.server.enums.TipoDeMensagem;
import redecolaborativa.server.excecoes.*;
import redecolaborativa.server.sistema.Usuario;


/**
 * Classe que representa um Item.
 * @author Isabelly Rocha, Lilia Sampaio e Pedro Adelino.
 */
public class Item implements Comparable<Item>, Serializable {
	private static final long serialVersionUID = 1L;
	private static int RETORNA_ID = 1;
	private List<Usuario> interessados;
	private boolean disponibilidade, devolvido;
	private String nome, descricao;
	private List<String> categorias;
	private Usuario dono;
	private GregorianCalendar dataDeCriacao;
	private int id;
	
	/**
	 * Cria um objeto do tipo Item.
	 * @param nome Nome do Item
	 * @param categorias Categorias do Item
	 * @param descricao Descricao do Item
	 * @param dono Usuario dono do item
	 * @throws NomeInvalidoException Se o nome for nulo ou vazio
	 * @throws CategoriaInvalidaException Se a categoria for nulo
	 * @throws DescricaoInvalidaException Se a descricao for nulo ou vazio
	 * @throws DonoInvalidoException Se o dono for nulo
	 */
	public Item(String nome, List<String> categorias, String descricao, Usuario dono) throws NomeInvalidoException, CategoriaInvalidaException, DescricaoInvalidaException, DonoInvalidoException {
		if(nome == null || nome.trim().isEmpty()) {
			throw new NomeInvalidoException("Nome inválido");
		} if(categorias == null) {
			throw new CategoriaInvalidaException("Categoria inválida.");
		} if(descricao == null || descricao.trim().isEmpty()) {
			throw new DescricaoInvalidaException("Descricao nao pode ser nulo ou vazio.");
		} if (dono == null) {
			throw new DonoInvalidoException("Dono nao pode ser nulo.");
		}
		this.nome = nome;
		this.categorias = categorias;
		this.descricao = descricao;
		this.dono = dono;
		disponibilidade = true;
		interessados = new LinkedList<Usuario>();
		dataDeCriacao = new GregorianCalendar();
		id = RETORNA_ID++;
	}
	
	/**
	 * Reseta gerador do id
	 */
	public static void resetaGeradorID() {
		RETORNA_ID = 1;
	}
	
	/**
	 * Recupera o nome de Item.
	 * @return O nome do Item
	 */
	public String getNome() {
		return nome;
	}
	
	/**
	 * Recupera a categoria do Item.
	 * @return A categoria do Item
	 */
	public List<String> getCategoria() {
		return categorias;
	}
	
	/**
	 * Recupera a descricao do Item.
	 * @return A descricao do Item
	 */
	public String getDescricao() {
		return descricao;
	}
	
	/**
	 * Recupera o usuario dono do item.
	 * @return O usuario dono do item
	 */
	public Usuario getDono() {
		return dono;
	}
	
	/**
	 * Recupera o id do item.
	 * @return O id do item
	 */
	public int getID() {
		return id;
	}
	
	/**
	 * Recupera a lista de interessados no item
	 * @return A lista de interessados no item
	 */
	public List<Usuario> getInteressados(){
		return new LinkedList<Usuario>(interessados);
	}
	
	/**
	 * Recupera a data de criacao do item.
	 * @return A data de criacao do item
	 */
	public GregorianCalendar getDataDeCriacao() {
		return dataDeCriacao;
	}
	
	/**
	 * Recupera um atributo do item do tipo dado no parametro.
	 * @param atributo Tipo do atributo desejado.
	 * @return Uma string com o atributo.
	 * @throws AtributoInvalidoException Caso o atributo passado seja nulo ou vazio.
	 */
	public String getAtributo(String atributo) throws AtributoInvalidoException {
		if(atributo == null || atributo.trim().equals("")) {
			throw new AtributoInvalidoException("Atributo inválido");
		} if(atributo.equals("nome")) {
			return nome;
		} if(atributo.equals("descricao")) {
			return descricao;
		} if(atributo.equals("categoria")) {
			return categorias();
		} throw new AtributoInvalidoException("Atributo inexistente");
	}
	
	/**
	 * Verifica se o item esta disponivel.
	 * @return True se estiver disponivel e false em caso contrario
	 */
	public boolean estaDisponivel() {
		return disponibilidade;
	}
	
	/**
	 * Informa se um item ja foi devolvido. 
	 * @return True se foi devolvido, false caso contrario.
	 */
	public boolean foiDevolvido() {
		return devolvido;
	}
	
	/**
	 * Muda o status de disponibilidade de um item, se mudar pra true o item eh adicionado a 
	 * lista de notificacoes de itens livres dos usuarios interessado naquele item.
	 * @param Disponibilidade True se estiver disponivel e false em caso contrario
	 * @throws DescricaoInvalidaException 
	 * @throws AssuntoInvalidoException 
	 * @throws TipoDeMensagemInvalido 
	 */
	public void setDisponibilidade(boolean disponibilidade) throws TipoDeMensagemInvalido, AssuntoInvalidoException, DescricaoInvalidaException {
		this.disponibilidade = disponibilidade;
		if (estaDisponivel()) {
			Iterator<Usuario> it = interessados.iterator();
			while (it.hasNext()) {
				Usuario usuario  = it.next();
				usuario.gerenteDeItens().recebeNotificacaoDeItemLivre(this);
				String assunto =  String.format("O item %s do usuário %s está disponível", nome, dono.getNome());
				dono.enviaMensagem(TipoDeMensagem.OFFTOPIC, assunto, assunto, usuario);
			}
		}
	}
	
	/**
	 * Modifica o status de devolucao do item.
	 * @param devolvido Novo status de devolucao do item.
	 */
	public void setDevolucao(boolean devolvido) {
		this.devolvido = devolvido;
	}
	
	/**
	 * Adiciona um usuario a lista de interessados no item.
	 * @param O usuario interessado no item
	 */
	public void adicionaInteressado(Usuario usuario) throws PermissaoNegadaException {
		if(interessados.contains(usuario)) {
			throw new PermissaoNegadaException("O usuário já registrou interesse neste item");
		} interessados.add(usuario);
	}
	
	/**
	 * Verifica se um usuario estava interessado no item e remove caso ele estivesse.
	 * @param usuario Usuario e ser verificado.
	 */
	public void removeInteressado(Usuario usuario) {
		if(interessados.contains(usuario)) {
			interessados.remove(usuario);
		} 
	}
	
	/**
	 * Compara dois objetos do tipo item.
	 * @param Item a ser comparado
	 * @return True se possuirem todos atributos iguais e false em caso contrario
	 */
	@Override
	public boolean equals(Object objeto) {
		if (!(objeto instanceof Item)) {
			return false;
		} 
		Item item = (Item) objeto;
		return item.getNome().equals(nome) && item.getCategoria().equals(categorias)
			   && item.getDescricao().equals(descricao) && item.getDono().equals(dono);
	}
	
	/**
	 * Compara dois objetos do tipo item atraves de suas datas de criação.
	 * @param Item a ser comparado
	 * @return Um numero menor que zero se o objeto for menor do que o que esta sendo comparado,
	 * maior que zero se for maior e zero pro caso deles serem iguais.
	 */
	@Override
	public int compareTo(Item item) {
		return dataDeCriacao.compareTo(item.dataDeCriacao);
	}
	
	/**
	 * Pega as categorias do item
	 * @return String contendo as categorias do item
	 */
	public String categorias(){
		String retorno = "";
		for(int i = 0; i < categorias.size(); i++){
			retorno += categorias.get(i) + ",";
		} return retorno.substring(0, retorno.length()-1);
	}
}